Skip to content

take into account the num of processes by ulimit #143614

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

klim-iv
Copy link

@klim-iv klim-iv commented Jul 7, 2025

No description provided.

@rustbot
Copy link
Collaborator

rustbot commented Jul 7, 2025

r? @tgross35

rustbot has assigned @tgross35.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added O-unix Operating system: Unix-like S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jul 7, 2025
@klim-iv
Copy link
Author

klim-iv commented Jul 7, 2025

due to ulimit restrictions is not used in std::thread
apears crashes like this one:
thread 'main' panicked at .../.cargo/registry/.../rayon-core-1.12.1/src/registry.rs:168:10:
The global thread pool has not been initialized.: ThreadPoolBuildError
 { kind: IOError(Os { code: 11, kind: WouldBlock, message: "Resource temporarily unavailable" }) }

@@ -440,13 +440,24 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> {
}
}
}

let mut ulimit = u64::MAX;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type is rlim_t, this won't work on x86-32

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment on lines 445 to 446
#[cfg(any(target_os = "android", target_os = "linux"))]
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The above block has the same cfg, this can be combined

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@tgross35
Copy link
Contributor

tgross35 commented Jul 7, 2025

Could you please put a more detailed description of how the problem is reproduced in the PR description? Or open a separate issue for it.

The change here also needs comments, and the docs at https://doc.rust-lang.org/beta/std/thread/fn.available_parallelism.html should be updated if this winds up being accepted.

Since this is a user-visible change this will need t-libs signoff, but first cc @cuviper since it looks like the issue shows up in Rayon (possibly rayon-rs/rayon#694?)

@rust-log-analyzer

This comment has been minimized.

@cuviper
Copy link
Member

cuviper commented Jul 7, 2025

I doubt that this will be useful -- e.g. starting a number of threads equal to the current RLIMIT_NPROC is already doomed by the existence of the current thread, nevermind any other threads or even other processes.

let mut r: libc::rlimit = unsafe { mem::zeroed() };
unsafe {
if libc::getrlimit(libc::RLIMIT_NPROC, &mut r) == 0 {
ulimit = r.rlim_max
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note, the "soft" rlim_cur is the value that is currently enforced, while the "hard" rlim_max is a limit on how high an unprivileged process can adjust its own rlim_cur.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@klim-iv
Copy link
Author

klim-iv commented Jul 8, 2025

I doubt that this will be useful -- e.g. starting a number of threads equal to the current RLIMIT_NPROC is already doomed by the existence of the current thread, nevermind any other threads or even other processes.

I see, patchset updated according this comment (will count working threads)

@klim-iv
Copy link
Author

klim-iv commented Jul 8, 2025

Could you please put a more detailed description of how the problem is reproduced in the PR description? Or open a separate issue for it.

The change here also needs comments, and the docs at https://doc.rust-lang.org/beta/std/thread/fn.available_parallelism.html should be updated if this winds up being accepted.

Since this is a user-visible change this will need libs-api signoff, but first cc @cuviper since it looks like the issue shows up in Rayon (possibly rayon-rs/rayon#694?)

bug here: rust-lang/rustup#4403

@rust-log-analyzer

This comment has been minimized.

@cuviper
Copy link
Member

cuviper commented Jul 8, 2025

I doubt that this will be useful -- e.g. starting a number of threads equal to the current RLIMIT_NPROC is already doomed by the existence of the current thread, nevermind any other threads or even other processes.

I see, patchset updated according this comment (will count working threads)

This makes it possible, as long as nothing else starts in the meantime, but I still don't think it's a good idea for a threadpool to completely and greedily claim the entire thread limit. (I also replied in rust-lang/rustup#4403 (comment).)

It would be better to set a limit like RAYON_NUM_THREADS that leaves sufficient headroom for the rest of your workload, across all of your processes, but that isn't something we can automatically determine from here. "Sufficient" may depend on a lot of factors, including future behavior.

Fundamentally, these resource limits are quite different than the basic "number of logical CPUs" view of available parallelism, because it's not really a problem to oversubscribe CPUs.

@klim-iv
Copy link
Author

klim-iv commented Jul 8, 2025

completely and greedily claim the entire thread limit

this patchset is not about 'greedily claim', here we are just return number of available parallel tasks
'greedily claim' appears in rayon, based on this info

anyway, I just want to help and show the problem, if you think that this issue is not "big deal" - ok, I just close request

@rust-log-analyzer

This comment has been minimized.

@cuviper
Copy link
Member

cuviper commented Jul 8, 2025

completely and greedily claim the entire thread limit

this patchset is not about 'greedily claim', here we are just return number of available parallel tasks 'greedily claim' appears in rayon, based on this info

Please understand, I did not mean any judgement in calling it "greedy".

It is the documented expectation of available_parallelism that this is a good number of threads to use, right in its headline: "Returns an estimate of the default amount of parallelism a program should use." That makes sense in general for the number of CPUs, but I would not recommend that a program should use their maximum RLIMIT_NPROC all at once.

anyway, I just want to help and show the problem, if you think that this issue is not "big deal" - ok, I just close request

I am just one voice here -- it's possible that other team members may feel differently.

due to ulimit restrictions is not used in std::thread
upears crashed like this one:
thread 'main' panicked at .../.cargo/registry/.../rayon-core-1.12.1/src/registry.rs:168:10:
The global thread pool has not been initialized.: ThreadPoolBuildError
 { kind: IOError(Os { code: 11, kind: WouldBlock, message: "Resource temporarily unavailable" }) }

resolve #143635
@tgross35
Copy link
Contributor

tgross35 commented Jul 8, 2025

I'm not sure what would be best here so
r? @cuviper

@rustbot rustbot assigned cuviper and unassigned tgross35 Jul 8, 2025
@cuviper cuviper added the I-libs-nominated Nominated for discussion during a libs team meeting. label Jul 8, 2025
@the8472
Copy link
Member

the8472 commented Jul 9, 2025

from the rustup issue:

For hosting my web projects I am using Linux on 3-rd party cloud provider
On that system installed 104 CPUs and also set maximum number of processes for user = 40 ( by ulimit -u)

nproc counts per-user threads, so it's a shared cap. An individual process can't really account for that and available_parallelism explicitly says

does not account for differences in (co)processor capabilities or current system load

I.e. won't look at what other processes are doing.

Maybe we can do something conservative here, if something sets a ridiculously tight nproc limit (<= number of CPUs) then we just clamp parallelism to 1 because we don't know what else might be running under that UID and we don't want to eat up all the threads.

Or maybe rayon should have a limited-effort mode where it spawns threads if it can but doesn't explode when the system doesn't let it.

@klim-iv
Copy link
Author

klim-iv commented Jul 17, 2025

one more issue related to wrong behavior of std::thread::available_parallelism in Linux systems with ulimit restrictions:
rust-lang/rustup#4416

@Amanieu
Copy link
Member

Amanieu commented Jul 17, 2025

We discussed this in the @rust-lang/libs meeting. There are many reasons why thread creation may fail, and fundamentally available_parallelism cannot cover all of them. In your particular case you have a rather contrived setup with a process count limit. Rather than trying to support all the ways in which parallelism can be limited, it would be better to instead offer a generic way to limit parallelism, possibly via an environment variable. This could be proposed via an ACP.

@rfcbot close

@rfcbot
Copy link
Collaborator

rfcbot commented Jul 17, 2025

Team member @Amanieu has proposed to close this. The next step is review by the rest of the tagged team members:

No concerns currently listed.

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@rfcbot rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-close This PR / issue is in PFCP or FCP with a disposition to close it. labels Jul 17, 2025
@Amanieu Amanieu removed the I-libs-nominated Nominated for discussion during a libs team meeting. label Jul 17, 2025
@cuviper
Copy link
Member

cuviper commented Jul 17, 2025

it would be better to instead offer a generic way to limit parallelism, possibly via an environment variable.

For example, this could be similar to RUST_MIN_STACK:
https://doc.rust-lang.org/nightly/std/thread/index.html#stack-size

@klim-iv
Copy link
Author

klim-iv commented Jul 17, 2025

I glad to listen that you drew attention to the problem
Looks like available_parallelism should be deleted at all, or updated it description with refactor all crates that using it (IMHO):

    Returns an estimate of the default amount of parallelism a program should use.

@the8472
Copy link
Member

the8472 commented Jul 17, 2025

Looks like available_parallelism should be deleted at all

Your use-case is uncommon, that is not sufficient motivation to deprecate an existing, widely-used std API.

Software looking at cpumasks, cgroups and number of processor cores and then returning the minimum of that is common practice. Java, C#, C++ all do some of that. None of them look at nprocs.

That is because nprocs is commonly set to a much larger number, and when it is limited it is really difficult to account for because it's shared across processes. It's more like running out of disk space.... you only notice when you hit the limit.

Using nprocs to limit shared workloads feels like a technique from the 90s. It's fine if you're running a bunch bunch of php or perl scripts or whatever. It doesn't interact well with modern multi-threaded runtimes.

One can write Rust like that, but most projects don't default to few-/single-threaded behavior and you have to explicitly opt in to that via arguments or environment variables.

I think adding an override for std would be reasonable since it'll avoid having to tweak things for each crate separately.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
disposition-close This PR / issue is in PFCP or FCP with a disposition to close it. O-unix Operating system: Unix-like proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants